
/* Author: Habeeb
 *
 * This file realises the probability distribution of the social interaction
 * It is a static class - i.e. do not need to instantiate it
 *
 * There are 3 clear distinctions to be made when probability is mentioned:
 *
 * -> the rateOfInfluence is a measure of how one can influence an event (this rate will change 
 *	  when one successfully influences or fails to influence another, affected by the eventOutcome)
 *
 * -> the rateAtWhichCanBeInfluenced is a measure of how one can be influenced due to the other character
 *	  (this parameter is affected by the eventOutcome directly)
 *
 * -> the eventOutcome is a measure of how much that event affects a character based on the above 2 factors
 *	  and with this eventOutcome, each character's rateOfInfluence or rateAtWhichCanBeInfluenced will change
 * 
 * 	  Prob(A | B) is defined as prob of A given the prob of B
 *	  and is computed with the inclusion of global set also
 *
 */

function probDistribution(){
	//console.log('the probability distribution class has been instantaited');
}


// This function will calculate the probability and return the result
// P(Influence outcome | (the characters's influence rate & the opposite charcacters' rate at which can be influenced) )
// the input will be: the rebel or army object, the citizen object
probDistribution.calculateProbBasedOnSingleObject = function(thisObjectThatAffectsProb, thisObjectToUpdate){
	var eventOutcome = null;
	
	// For the influenced person (citizen)
	if(thisObjectThatAffectsProb.getType() === "Rebel" || thisObjectThatAffectsProb.career === "Rebel"){
		eventOutcome = thisObjectToUpdate.rateAtWhichCanBeInfluenced + (thisObjectToUpdate.rateAtWhichCanBeInfluenced / (thisObjectToUpdate.rateAtWhichCanBeInfluenced + thisObjectThatAffectsProb.rateOfInfluence));
	
	} else if(thisObjectThatAffectsProb.getType() === "Army" || thisObjectThatAffectsProb.getType() === "Spy"){
		eventOutcome = thisObjectToUpdate.rateAtWhichCanBeInfluenced - (thisObjectToUpdate.rateAtWhichCanBeInfluenced / (thisObjectThatAffectsProb.rateOfInfluence + thisObjectToUpdate.rateAtWhichCanBeInfluenced));
	}
	
	eventOutcome = probDistribution.wrapSpecialValues(eventOutcome);	
	thisObjectToUpdate.arrayOfBeingInfluenced.push(eventOutcome);
	thisObjectToUpdate.eventOutcomeQueue.push(eventOutcome);
	probDistribution.performNormalDistribution(thisObjectToUpdate, eventOutcome);
	console.log('the eventOutcome is: ' + eventOutcome);
}

// Returns the total no of available people on the global scale for each character set
probDistribution.getTotalPeopleCountForThisType = function(thisType){
	switch(thisType){

		// All the paramters returned have global scope
		case 'Army': 		return army;

		case 'Rebel': 		return rebel;

		case 'Merchant': 	return merchant;

		case 'Spy': 		return spy;

		case 'Citizen': 	return citizen;

	}//switch case statement

}

// This function calaculates the radial distribution i.e. the rate of influence on the 
// surrounding people
probDistribution.calculateRadialDistribution = function(thisArray, thisWinnerOfFight){

	// Check if the array is not empty, then iterate through the Array to 
	// perform the computation for the citizens
	if(thisArray.length != 0){
		var distanceFromEvent = null;
		for(var iter=0; iter < thisArray.length; iter++){
			// Only citizens will get affected
			if(thisArray[iter].getType() === "Citizen"){
				// Get the distance from the event
				distanceFromEvent = thisArray[iter].distanceTo(thisWinnerOfFight);
				// Apply the exponential distribution
			 	probDistribution.performNormalDistribution(thisArray[iter], thisArray[iter].rateAtWhichCanBeInfluenced * Math.exp(-distanceFromEvent));
			}
		}//for loop statement

	}//if statement

}

// This function will normalise the influence distribution of the characters
probDistribution.performNormalDistribution = function(thisObject, thisEventOutcome){

	var arrayToUse, 
		totalOfObject = 0, 
		meanOfObject = 0, 
		stdDevOfObject = 0,
		calculatedDistributionValue = 0,
		localObjectType = thisObject.getType();
	
	// Get the type of array to use
	switch(localObjectType){
		case 'Rebel':
			arrayToUse = thisObject.arrayOfInfluence;
			break;
		case 'Spy':
			arrayToUse = thisObject.arrayOfInfluence;
			break;
		case 'Citizen':
			arrayToUse = thisObject.arrayOfBeingInfluenced;
			break;
        case 'Player' :
            arrayToUse = thisObject.arrayOfInfluence;
	}

	// Calculate the mean of the distribution
	for(var iter=0; iter<arrayToUse.length; iter++){
		totalOfObject += arrayToUse[iter];
	}
	meanOfObject = totalOfObject / arrayToUse.length;

	// Compute the standard deviation
	for(var iter=0; iter<arrayToUse.length; iter++){
		totalOfObject += Math.pow((arrayToUse[iter] - meanOfObject), 2);
	}
	stdDevOfObject = Math.sqrt(totalOfObject / arrayToUse.length);

	// Perform the normal distribution for the object
	calculatedDistributionValue = ( (1 / (stdDevOfObject * Math.sqrt(2*Math.PI))) * 
								    Math.exp(-((Math.pow(thisEventOutcome-meanOfObject, 2)) / ( 2 * (Math.pow(stdDevOfObject, 2))))) 
								  );

	// Update the correct objects distribtuion
	switch(localObjectType){
		case 'Rebel':
			thisObject.rateOfInfluence = probDistribution.wrapThisValue(calculatedDistributionValue);
			break;
		case 'Spy':
			thisObject.rateOfInfluence = probDistribution.wrapThisValue(calculatedDistributionValue);
			break;
        case 'Player':
            thisObject.rateOfInfluence = probDistribution.wrapThisValue(calculatedDistributionValue);
            break;
		case 'Citizen':
			thisObject.rateAtWhichCanBeInfluenced = probDistribution.wrapThisValue(calculatedDistributionValue);
			break;
	}
};

// This function will wrap the input values to be between 0 and 1 and
// will return the original value if it is already in between
probDistribution.wrapThisValue = function(thisValue){
	if(thisValue > 1.0){
		thisValue = 1.0;
	} else if(thisValue < 0){
		thisValue = 0;
	} else if (thisValue < 0.1){
		thisValue *= 10;
	}
	return thisValue;

};


probDistribution.wrapSpecialValues = function(thisValue){
	var localValue = thisValue;
	localValue = Math.abs(localValue);
	
	// Then wrap the value
	localValue = probDistribution.wrapThisValue(localValue);
	return localValue;
}

